/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.extend.action;

import com.inspur.edp.lcm.metadata.api.entity.ExtractContext;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackage;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.ProcessMode;
import com.inspur.edp.lcm.metadata.api.service.FileService;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.core.entity.MdProjDependencySource;
import com.inspur.edp.lcm.metadata.core.entity.MdProjectDependencyNode;
import com.inspur.edp.lcm.metadata.core.index.ProjectMetadataCacheService;
import com.inspur.edp.lcm.metadata.inner.api.IdeSettingService;
import com.inspur.edp.lcm.metadata.inner.api.data.DependencyMode;
import com.inspur.edp.lcm.metadata.inner.api.utils.IdeSettingUtils;
import com.inspur.edp.lcm.metadata.spi.ExtractAction;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ExtractActionImpl implements ExtractAction {
    private static Set<String> serverMetadataPackageFileNames = null;
    private final FileService fileService = SpringBeanUtils.getBean(FileService.class);

    private Set<String> getServerMetadataPackageFileNames() {
        if (serverMetadataPackageFileNames == null) {
            synchronized (ExtractActionImpl.class) {
                if (serverMetadataPackageFileNames == null) {
                    Set<String> tmp = new CopyOnWriteArraySet<>();
                    List<File> allFiles = FileServiceImp.getAllFiles(Utils.getServerMetadataPackDir(), true);
                    for (File file : allFiles) {
                        tmp.add(file.getName());
                    }
                    serverMetadataPackageFileNames = tmp;
                }
            }
        }
        return serverMetadataPackageFileNames;
    }

    @Override
    public void extract(ExtractContext context) {
        // 提取元数据包
        extractMdpkg(context);

        if (context.getDeployType() != null && !"all".equals(context.getDeployType()) && !context.getDeployType().contains("jar")) {
            return;
        }

        // 提取java交付物
        extractJavaProduct(context);

        // 若配置需要 则提取依赖元数据
        IdeSettingService ideSettingService = IdeSettingUtils.getIdeSettingService();
        if (Objects.isNull(ideSettingService)) {
            return;
        }
        DependencyMode dependencyMode = ideSettingService.getDependencyMode();
        if (Objects.isNull(dependencyMode)) {
            return;
        }
        if (dependencyMode == DependencyMode.EXTRACT_DEPEND_MDPKG) {
            extractMdpkgDependencies(context);
        }
    }

    /**
     * 提取依赖元数据至当前工程publish下
     *
     * @param context 提取信息,包含项目路径等信息
     * @author sunhongfei01
     */
    private void extractMdpkgDependencies(ExtractContext context) {
        MetadataProjectService metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        MetadataService metadataService = SpringBeanUtils.getBean(MetadataService.class);

        String metaProjPath = FileServiceImp.combinePath(context.getProjectPath(), Utils.getPublishPath(), "metadata/apps");
        // 获取当前元数据工程信息
        MetadataProject metadataProjInfo = metadataProjectService.getMetadataProjInfo(context.getProjectPath());
        ProjectMetadataCacheService projectMetadataCacheService = ProjectMetadataCacheService.getNewInstance(
                metadataProjInfo, context.getProjectPath());

        Map<String, MdProjectDependencyNode> allReferences = projectMetadataCacheService.getAllReferences();

        if (allReferences == null) {
            log.error("Error when copy depend mdpkg to public:failed when get MetaData [{}]/[{}] cacheInfo.",
                    metadataProjInfo.getName(), metadataProjInfo.getProjectPath());
            return;
        }

        // 遍历以来的元数据提取到publish目录下
        allReferences.forEach((refName, ref) -> {
            // 若来源为数据库 不做提取
            if (ref.getSourceName() == MdProjDependencySource.DataBase) {
                //TODO 是否需要把数据库中元数据包信息提取data?
                return;
            }

            // 获取依赖元数据
            String mdpkgFilePath = ref.getLocation();
            if (ref.getSourceName() == MdProjDependencySource.BO) {
                mdpkgFilePath = FileServiceImp.combinePath(mdpkgFilePath,
                        Utils.getMetadataProjPath(), Utils.getMetadataBinPath(),
                        ref.getMetadataPackageName() + Utils.getMetadataPackageExtension());
            }
            File mdpkgFile = new File(mdpkgFilePath);

            // 若元数据不存在,若为BO间依赖则抛出异常,否则不做处理
            if (!mdpkgFile.exists()) {
                if (ref.getSourceName() == MdProjDependencySource.BO) {
                    throw new RuntimeException("依赖的BO内元数据包不存在，请先打包: " + ref.getMetadataPackageName());
                }
                log.error("Error when copy depend mdpkg to public: Failed to find depend mdpkg file [{}]"
                        , mdpkgFile.getName());
                return;
            }

            // 当前依赖元数据包在 安装盘提供元数据包中,则不做提取
            if (getServerMetadataPackageFileNames().contains(mdpkgFile.getName())) {
                return;
            }

            // 获取依赖元数据所在目录
            String fromPath = mdpkgFile.getParent();
            // 读取依赖元数据的包信息 获取其 appcode/sucode
            MetadataPackage packageInfo = metadataService.getMetadataPackageInfo(mdpkgFilePath, fromPath);

            String appCode = packageInfo.getServiceUnitInfo().getAppCode().toLowerCase();
            String suCode = packageInfo.getServiceUnitInfo().getServiceUnitCode().toLowerCase();
            String curMdPkgTargetDir = FileServiceImp.combinePath(metaProjPath, appCode, suCode, Utils.getMetadataProjPath());
            File mdPkgTargetDir = new File(curMdPkgTargetDir);
            if (!mdPkgTargetDir.exists() && !mdPkgTargetDir.mkdirs()) {
                // 若mdpkg目标目录创建不成功
                log.error("Error when copy depend mdpkg to public: failed when create directory [{}]", curMdPkgTargetDir);
                return;
            }
            curMdPkgTargetDir = FileServiceImp.combinePath(curMdPkgTargetDir, mdpkgFile.getName());

            try {
                fileService.fileCopy(mdpkgFilePath, curMdPkgTargetDir);
            } catch (IOException e) {
                log.error("Error when copy depend mdpkg to public: failed when copy file [{}] to [{}]", mdpkgFilePath, curMdPkgTargetDir);
                log.error("copy file error stack", e);
            }
        });
    }

    private void extractJavaProduct(ExtractContext context) {

        String javaCodePath = Paths.get(context.getProjectPath()).resolve(Utils.getJavaProjectPath()).resolve(Utils.getCodePath()).toString();
        String deployPath = Paths.get(context.getDeployPath()).resolve(Utils.getLibsPath()).toString();

        MetadataProjectService metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        ProcessMode processMode = metadataProjectService.getProcessMode(context.getProjectPath());
        if (ProcessMode.generation.equals(processMode)) {
            copyJar(javaCodePath, "api", deployPath);
            copyJar(javaCodePath, "core", deployPath);
        }

        copyJar(javaCodePath, "comp", deployPath);
    }

    private void copyJar(String javaCodePath, String module, String deployPath) {
        String moduleJarPath = getJarPath(javaCodePath, module);
        if (moduleJarPath.isEmpty()) {
            return;
        }
        String destPath = Paths.get(deployPath).resolve(new File(moduleJarPath).getName()).toString();
        fileService.createDirectory(deployPath);
        try {
            fileService.fileCopy(moduleJarPath, destPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String getJarPath(String javaCodePath, String module) {
        String targetPath = Paths.get(javaCodePath).resolve(module).resolve(Utils.getTargetPath()).toString();
        File file = new File(targetPath);
        if (file.exists()) {
            File[] files = file.listFiles((dir, name) -> name.endsWith(Utils.getJarSuffix()));
            if (files != null && files.length > 0) {
                return files[0].getPath();
            }
        }
        System.out.println("无" + module + "jar包。");
        return "";
    }

    private void extractMdpkg(ExtractContext context) {
        String binPath = Paths.get(context.getProjectPath()).resolve(Utils.getMetadataProjPath()).resolve(Utils.getMetadataBinPath()).toString();
        File binFile = new File(binPath);
        String sourcePath = "";
        if (binFile.exists()) {
            File[] files = binFile.listFiles((dir, name) -> name.endsWith(Utils.getMetadataPackageExtension()));
            if (files != null && files.length > 0) {
                sourcePath = files[0].getPath();
            }
        }

        if (sourcePath.isEmpty()) {
            throw new RuntimeException("元数据包不存在，请先生成元数据包：" + context.getProjectPath());
        }
        String targetDir = FileServiceImp.combinePath(
                context.getDeployPath().replace("publish\\" + Utils.getServerPath() + "\\apps", "publish\\metadata\\apps")
                        .replace("publish/" + Utils.getServerPath() + "/apps", "publish/metadata/apps")
                , "metadata");
        String targetPath = FileServiceImp.combinePath(targetDir, new File(sourcePath).getName());
        fileService.createDirectory(targetDir);
        try {
            fileService.fileCopy(sourcePath, targetPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
